// Copyright 2023 The Centipede Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef THIRD_PARTY_CENTIPEDE_PC_INFO_H_
#define THIRD_PARTY_CENTIPEDE_PC_INFO_H_

#include <cstddef>
#include <cstdint>
#include <istream>
#include <ostream>
#include <string>
#include <vector>

namespace fuzztest::internal {

// PCInfo is a pair {PC, bit mask with PC flags}.
// PCInfo objects are generated by the compiler and can be extracted from the
// binary, see https://clang.llvm.org/docs/SanitizerCoverage.html#pc-table.
// PCInfo objects can also be created by analyzing the binary with objdump.
struct PCInfo {
  enum PCFlags : uintptr_t {
    kFuncEntry = 1 << 0,  // The PC is the function entry block.
  };

  uintptr_t pc{};
  uintptr_t flags{};

  bool has_flag(PCFlags f) const { return flags & f; }

  bool operator==(const PCInfo &rhs) const;
};

// Array of PCInfo-s.
// PCTable is created by the compiler/linker in the instrumented binary.
// The order of elements is significant: each element corresponds
// to the coverage counter with the same index.
// Every PCInfo that is kFuncEntry is followed by PCInfo-s from the same
// function.
using PCTable = std::vector<PCInfo>;

// Reads a PCTable from `in`, returns it. Returns empty table on error.
PCTable ReadPcTable(std::istream &in);

// Writes the contents of `pc_table` in the format expected by `ReadPCTable`.
void WritePcTable(const PCTable &pc_table, std::ostream &out);

// PCGuard is used during run-time as a compressed reference to PCInfo.
// The SanitizerCoverage's 'Tracing PCs with guards'
// (https://clang.llvm.org/docs/SanitizerCoverage.html#tracing-pcs-with-guards)
// passes a pointer to a 32-bit object, unique for every instrumented PC.
// At the DSO initialization time, we initialize these unique 32-bit objects
// to represent the information about the PCs.
struct PCGuard {
  // True if this is a function's entry PC.
  // This information may not be available, and so the code should tolerate
  // the situation where no PC is marked as a function entry.
  uint32_t is_function_entry : 1;
  // The index of the PC.
  uint32_t pc_index : 31;

  // pc_index is 31-bit, so we can't have more than this number of PCs.
  static constexpr size_t kMaxNumPCs = 1ULL << 31;

  // Invalid value of pc_index.
  static constexpr uint32_t kInvalidPcIndex = kMaxNumPCs - 1;

  // Returns true if `*this` is valid.
  bool IsValid() const { return pc_index != kInvalidPcIndex; }
};

// DsoInfo represents a single SanCov-instrumented DSO (library or main binary).
struct DsoInfo {
  // Path to the file on disk, which can be used for symbolization.
  std::string path;

  // Number of SanCov-instrumented PCs in this DSO.
  size_t num_instrumented_pcs = 0;
};

// Array of DsoInfo.
using DsoTable = std::vector<DsoInfo>;

}  // namespace fuzztest::internal

#endif  // THIRD_PARTY_CENTIPEDE_PC_INFO_H_
